﻿namespace Arms.Application.Modules.Orders
{
    using Arms.Application.Modules.Orders.DTOs;
    using Arms.Application.Services;
    using Arms.Domain;
    using Arms.Domain.Modules.DiningRoom;
    using Arms.Domain.Modules.Orders;
    using Arms.Domain.Modules.People;
    using Arms.Domain.Modules.Products;
    using Arms.Framework;
    using System;
    using System.Linq;

    public class OrderService : ServiceBase, IOrderService
    {
        public OrderService(IUnitOfWork uow) : base(uow) { }

        public int CreateOrderHeader(DateTime orderDate, int? customerID, int? diningRoomTableID)
        {
            OrderHeader orderHeader = new OrderHeader() { OrderDate = orderDate };

            if (customerID.HasValue && customerID > 0 && base.UnitOfWork.Repository<User>().Any(x => x.ID == customerID))
            {
                orderHeader.CustomerID = customerID;
            }

            if (diningRoomTableID.HasValue && diningRoomTableID > 0 && base.UnitOfWork.Repository<DiningRoomTable>().Any(x => x.ID == diningRoomTableID))
            {
                orderHeader.DiningRoomTableID = diningRoomTableID;
            }

            base.UnitOfWork.Repository<OrderHeader>().Insert(orderHeader);
            base.UnitOfWork.Commit();
            return orderHeader.ID;
        }

        public int AddOrderToOrderHeader(DateTime orderDate, int orderHeaderID, DateTime? deliveryDate, DateTime? pickupDate)
        {
            if (base.UnitOfWork.Repository<OrderHeader>().Any(x => x.ID == orderHeaderID))
            {
                Order order;
                if (deliveryDate.HasValue)
                {
                    order = new DeliveryOrder() { OrderHeaderID = orderHeaderID, OrderState = OrderState.Open, OrderType = OrderType.Delivery, DeliveryDate = deliveryDate.Value };
                }
                else if (pickupDate.HasValue)
                {
                    order = new PickupOrder() { OrderHeaderID = orderHeaderID, OrderState = OrderState.Open, OrderType = OrderType.Pickup, PickupDate = pickupDate.Value };
                }
                else
                {
                    order = new DiningRoomOrder() { OrderHeaderID = orderHeaderID, OrderState = OrderState.Open, OrderType = OrderType.DiningRoom };
                }

                order.CalculateOrderNumber();
                order.OrderDate = orderDate;
                base.UnitOfWork.Repository<Order>().Insert(order);
                base.UnitOfWork.Commit();

                return order.ID;
            }

            return 0;
        }

        public void RemoveOrderFromOrderHeader(int orderHeaderID, int orderID)
        {
            OrderHeader orderHeader = base.UnitOfWork.Repository<OrderHeader>().Find(orderHeaderID);
            
            if (orderHeader != null)
            {
                orderHeader.Orders.RemoveWhere(x => x.ID == orderID);
                base.UnitOfWork.Repository<OrderHeader>().Update(orderHeader);
                base.UnitOfWork.Commit();
            }
        }

        public void VoidOrderHeader(int orderHeaderID)
        {
            OrderHeader orderHeader = base.UnitOfWork.Repository<OrderHeader>().Find(orderHeaderID);

            if (orderHeader != null)
            {
                foreach (var order in orderHeader.Orders)
                {
                    VoidOrder(order.ID);
                }
            }
        }

        public void InvoiceOrderHeader(int orderHeaderID)
        {
            OrderHeader orderHeader = base.UnitOfWork.Repository<OrderHeader>().Find(orderHeaderID);

            if (orderHeader != null)
            {
                foreach (var order in orderHeader.Orders)
                {
                    InvoiceOrder(order.ID);
                }
            }
        }

        public void AddProductsToOrder(int orderID, params ProductsInOrderDTO[] productsInOrders)
        {
            Order order = base.UnitOfWork.Repository<Order>().Find(orderID);

            if (order != null)
            {
                foreach (var productsInOrder in productsInOrders)
                {
                    Product product = base.UnitOfWork.Repository<Product>().Find(productsInOrder.ProductID);

                    if (product != null)
                    {
                        if (order.ProductsInOrders.Select(x => x.ProductID).Contains(product.ID))
                        {
                            order.ProductsInOrders.SingleOrDefault(x => x.ProductID == product.ID).Quantity += productsInOrder.Quantity;
                        }
                        else
                        {
                            order.ProductsInOrders.Add(new ProductsInOrder() { ProductID = productsInOrder.ProductID, Quantity = productsInOrder.Quantity });
                        }
                    }
                }
                base.UnitOfWork.Repository<Order>().Update(order);
                base.UnitOfWork.Commit();

                order.CalculateSubTotal();
                base.UnitOfWork.Repository<Order>().Update(order);
                base.UnitOfWork.Commit();
            }
        }

        public void RemoveProductsFromOrder(int orderID, params ProductsInOrderDTO[] productsInOrders)
        {
            Order order = base.UnitOfWork.Repository<Order>().Find(orderID);

            if (order != null)
            {
                order.ProductsInOrders.RemoveWhere(x => productsInOrders.Select(y => y.ProductID).Contains(x.ID));
                base.UnitOfWork.Repository<Order>().Update(order);
                base.UnitOfWork.Commit();

                order.CalculateSubTotal();
                base.UnitOfWork.Repository<Order>().Update(order);
                base.UnitOfWork.Commit();
            }
        }

        public void VoidOrder(int orderID)
        {
            Order order = base.UnitOfWork.Repository<Order>().Find(orderID);

            if (order != null)
            {
                order.OrderState = OrderState.Void;
                base.UnitOfWork.Repository<Order>().Update(order);
                base.UnitOfWork.Commit();
            }
        }

        public void InvoiceOrder(int orderID)
        {
            Order order = base.UnitOfWork.Repository<Order>().Find(orderID);

            if (order != null)
            {
                order.OrderState = OrderState.Invoiced;
                base.UnitOfWork.Repository<Order>().Update(order);
            }
        }


        public int AddOrderToOrderHeader(DateTime orderDate, int orderHeaderID)
        {
            throw new NotImplementedException();
        }
    }
}
